package org.abc.fund.service.dataSourceCollection;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.abc.fund.dto.dataSource.JoinQuantResultDTO;
import org.abc.fund.entity.funds.*;
import org.abc.fund.repository.funds.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
 * 聚源API获取基金数据保存
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class FundDataPersistenceService {
    private final FundNetValueHistoryRepository fundNetValueHistoryRepository;
    private final FundFinIndicatorRepository fundFinIndicatorRepository;
    private final FundDividendRepository fundDividendRepository;
    private final FundMfDailyProfitRepository fundMfDailyProfitRepository;
    private final FundMtssRepository fundMtssRepository;
    private final FundHoldingRepository fundHoldingRepository;
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    //主入口方法
    @Transactional
    public int saveData(JoinQuantResultDTO result) {
        if (result == null || !"success".equalsIgnoreCase(result.getStatus())) {
            log.warn("执行失败");
            return 0;
        }
        String dataType = result.getDataType();
        if (result.getRawData() != null && !result.getRawData().isEmpty()) {
            return saveRawData(dataType, result.getRawData());
        } else {
            return 0;
        }
    }

    //数据类型路由
    private int saveRawData(String dataType, List<Map<String, Object>> rawData) {
        switch (dataType) {
            case "FUND_HOLDING":
                return saveFundHolding(rawData);
            case "FUND_DIVIDEND":
                return saveFundDividendFromRaw(rawData);
            case "FUND_FIN_INDICATOR":
                return saveFundFinIndicatorFromRaw(rawData);
            case "FUND_MF_DAILY_PROFIT":
                return saveFundMfDailyProfitFromRaw(rawData);
            case "FUND_NET_VALUE":
                return saveFundNetValueFromRaw(rawData);
            case "FUND_MTSS":
                return saveFundMtssFromRaw(rawData);
            default:
                log.warn("未实现该数据类型保存{}", dataType);
                return 0;
        }
    }

    //数据日期解析
    private LocalDate parseLocalDate(String dateStr) {
        if (dateStr == null || dateStr.isBlank()) {
            return null;
        }
        try {
            return LocalDate.parse(dateStr, DATE_FORMATTER);
        } catch (Exception e) {
            log.warn("不可以转化该日期格式'{}'", dateStr);
            return null;
        }
    }
    
    //日期解析
    private LocalDate parseLocalDateFromObject(Object dateObj) {
        if (dateObj == null) {
            return null;
        }
        if (dateObj instanceof LocalDate) {
            return (LocalDate) dateObj;
        }
        if (dateObj instanceof String) {
            return parseLocalDate((String) dateObj);
        }
        log.warn("无法解析的日期对象类型：{}", dateObj.getClass().getName());
        return null;
    }

    //解析数值对象（支持Integer、Long、Double、BigDecimal）
    private BigDecimal parseBigDecimal(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        if (value instanceof Number) {
            return BigDecimal.valueOf(((Number) value).doubleValue());
        }
        if (value instanceof String) {
            try {
                return new BigDecimal((String) value);
            } catch (NumberFormatException e) {
                log.warn("解析失败'{}'", value);
                return null;
            }
        }
        return null;
    }

    //保存持仓数据
    private int saveFundHolding(List<Map<String, Object>> rawData) {
        if (rawData == null || rawData.isEmpty()) {
            log.warn("无持仓数据raw");
            return 0;
        }
        List<FundHolding> list = rawData.stream().map(data -> {
            FundHolding h = new FundHolding();
            h.setFundCode((String) data.get("fund_code"));
            h.setStockName((String) data.get("stock_name"));
            h.setStockCode((String) data.get("stock_code"));
            h.setHoldingRatio(parseBigDecimal(data.get("holding_ratio")));
            h.setProportion(h.getHoldingRatio());
            h.setMarketValue(parseBigDecimal(data.get("market_value")));
            String reportDateStr = (String) data.get("report_date");
            if (reportDateStr != null) {
                h.setReportDate(reportDateStr);
                try {
                    LocalDate d = LocalDate.parse(reportDateStr);
                    h.setReportDateLocalDate(d);
                    h.setPeriodEndDate(d);
                } catch (Exception e) {
                    log.warn("报告期字段 report_date 值无效：{}", reportDateStr);
                }
            }
            return h;
        }).filter(Objects::nonNull).toList();
        return fundHoldingRepository.saveAll(list).size();
    }

    //保存基金分红拆分合并信息
    private int saveFundDividendFromRaw(List<Map<String, Object>> rawData) {
        if (rawData == null || rawData.isEmpty()) {
            log.warn("无数据保存");
            return 0;
        }
        List<FundDividend> entities = rawData.stream().map(map -> {
            String code = (String) map.get("code");
            if (code == null || code.isBlank()) {
                return null;
            }
            Object pubDateObj = map.get("pub_date");
            LocalDate pubDate = parseLocalDateFromObject(pubDateObj);
            if (pubDate == null) {
                return null;
            }
            FundDividend entity = fundDividendRepository.findByFundCodeAndPubDate(code, pubDate)
                    .orElse(new FundDividend());
            entity.setFundCode(code);
            entity.setPubDate(pubDate);
            entity.setExDividendDate(parseLocalDateFromObject(map.get("ex_dividend_date")));
            entity.setRecordDate(parseLocalDateFromObject(map.get("record_date")));
            entity.setPaymentDate(parseLocalDateFromObject(map.get("payment_date")));
            entity.setDividend(parseBigDecimal(map.get("dividend")));
            entity.setSplitRatio((String) map.get("split_ratio"));
            entity.setMergeRatio((String) map.get("merge_ratio"));
            entity.setDividendType((String) map.get("dividend_type"));
            return entity;
        }).filter(Objects::nonNull)
          .filter(e -> e.getPubDate() != null)
          .toList();
        if (entities.isEmpty()) {
            return 0;
        }
        try {
            return fundDividendRepository.saveAll(entities).size();
        } catch (Exception e) {
            log.error("保存失败 {}", e.getMessage(), e);
            throw new RuntimeException("保存基金分红数据失败", e);
        }
    }
    
    //保存基金财务指标（FUND_FIN_INDICATOR）
    private int saveFundFinIndicatorFromRaw(List<Map<String, Object>> rawData) {
        if (rawData == null || rawData.isEmpty()) {
            log.warn("无基金财务数据");
            return 0;
        }
        List<FundFinIndicator> entities = rawData.stream().map(map -> {
            String code = (String) map.get("code");
            if (code == null || code.isBlank()) {
                return null;
            }
            Object pubDateObj = map.get("pub_date");
            LocalDate statisticDate = parseLocalDateFromObject(pubDateObj);
            if (statisticDate == null) {
                statisticDate = parseLocalDateFromObject(map.get("period_end"));
            }
            if (statisticDate == null) {
                log.warn("跳过(pub_date or period_end) for code: {}", code);
                return null;
            }
            FundFinIndicator entity = fundFinIndicatorRepository.findByFundCodeAndStatisticDate(code, statisticDate)
                    .orElse(new FundFinIndicator());
            entity.setFundCode(code);
            entity.setStatisticDate(statisticDate);
            entity.setTotalAsset(parseBigDecimal(map.get("total_asset")));
            BigDecimal totalAsset = entity.getTotalAsset();
            BigDecimal netAsset = parseBigDecimal(map.get("nav"));
            if (totalAsset != null && netAsset != null) {
                entity.setTotalLiability(totalAsset.subtract(netAsset));
            } else {
                entity.setTotalLiability(parseBigDecimal(map.get("total_liability")));
            }
            if (netAsset == null) {
                netAsset = parseBigDecimal(map.get("net_asset"));
            }
            entity.setNetAsset(netAsset);
            entity.setFundShares(parseBigDecimal(map.get("total_tna")));
            entity.setPeriodReturn(parseBigDecimal(map.get("period_return")));
            entity.setNavGrowthRate(parseBigDecimal(map.get("nav_growth")));
            return entity;
        }).filter(Objects::nonNull)
          .filter(e -> e.getStatisticDate() != null)
          .toList();
        if (entities.isEmpty()) {
            return 0;
        }
        
        try {
            return fundFinIndicatorRepository.saveAll(entities).size();
        } catch (Exception e) {
            log.error("保存基金财务指标数据失败：{}", e.getMessage(), e);
            throw new RuntimeException("保存基金财务指标数据失败", e);
        }
    }
    
    //保存货币基金收益日报（FUND_MF_DAILY_PROFIT）
    private int saveFundMfDailyProfitFromRaw(List<Map<String, Object>> rawData) {
        if (rawData == null || rawData.isEmpty()) {
            log.warn("没有获取到货币基金收益原始数据");
            return 0;
        }
        List<FundMfDailyProfit> entities = rawData.stream().map(map -> {
            String code = (String) map.get("code");
            if (code == null || code.isBlank()) {
                return null;
            }
            Object endDateObj = map.get("end_date");
            LocalDate endDate = parseLocalDateFromObject(endDateObj);
            if (endDate == null) {
                return null;
            }
            FundMfDailyProfit entity = fundMfDailyProfitRepository.findByFundCodeAndEndDate(code, endDate)
                    .orElse(new FundMfDailyProfit());
            entity.setFundCode(code);
            entity.setEndDate(endDate);
            entity.setDailyProfit(parseBigDecimal(map.get("daily_profit")));
            entity.setSevenDayYield(parseBigDecimal(map.get("seven_day_yield")));
            entity.setRealizedProfit(parseBigDecimal(map.get("realized_profit")));
            return entity;
        }).filter(Objects::nonNull)
          .filter(e -> e.getEndDate() != null)
          .toList();
        if (entities.isEmpty()) {
            return 0;
        }
        try {
            return fundMfDailyProfitRepository.saveAll(entities).size();
        } catch (Exception e) {
            log.error("保存货币基金收益数据失败：{}", e.getMessage(), e);
            throw new RuntimeException("保存货币基金收益数据失败", e);
        }
    }
    
    //保存基金净值信息（FUND_NET_VALUE）
    private int saveFundNetValueFromRaw(List<Map<String, Object>> rawData) {
        if (rawData == null || rawData.isEmpty()) {
            log.warn("没有获取到净值原始数据");
            return 0;
        }
        List<FundNetValueHistory> entities = rawData.stream().map(map -> {
            String code = (String) map.get("code");
            if (code == null || code.isBlank()) {
                return null;
            }
            Object dayObj = map.get("day");
            LocalDate endDate = parseLocalDateFromObject(dayObj);
            if (endDate == null) {
                log.warn("基金 {} 的净值日期解析失败，已跳过", code);
                return null;
            }
            FundNetValueHistory entity = fundNetValueHistoryRepository.findByFundCodeAndNetDate(code, endDate)
                    .orElse(new FundNetValueHistory());
            entity.setFundCode(code);
            entity.setNetDate(endDate);
            BigDecimal unitNetValue = parseBigDecimal(map.get("net_value"));
            entity.setUnitNav(unitNetValue);
            BigDecimal accNetValue = parseBigDecimal(map.get("sum_value"));
            entity.setAccumulatedNav(accNetValue);
            entity.setAdjustedNetValue(parseBigDecimal(map.get("refactor_net_value")));
            entity.setFactor(parseBigDecimal(map.get("factor")));
            entity.setAccFactor(parseBigDecimal(map.get("acc_factor")));
            return entity;
        }).filter(Objects::nonNull)
          .filter(e -> e.getNetDate() != null)
          .toList();
        if (entities.isEmpty()) {
            log.warn("未生成需要保存的净值实体列表");
            return 0;
        }
        calculateDailyGrowthRate(entities);
        try {
            return fundNetValueHistoryRepository.saveAll(entities).size();
        } catch (Exception e) {
            log.error("保存基金净值数据出错：{}", e.getMessage(), e);
            throw new RuntimeException("保存基金净值数据失败", e);
        }
    }

    //保存融资融券信息（FUND_MTSS）
    private int saveFundMtssFromRaw(List<Map<String, Object>> rawData) {
        if (rawData == null || rawData.isEmpty()) {
            log.warn("没有获取到融资融券原始数据");
            return 0;
        }
        List<FundMtss> entities = rawData.stream().map(map -> {
            String code = (String) map.get("sec_code");
            if (code == null || code.isBlank()) {
                code = (String) map.get("code");
            }
            if (code == null || code.isBlank()) {
                log.warn("记录缺少基金代码，已跳过。可用字段：{}", map.keySet());
                return null;
            }
            Object dateObj = map.get("date");
            LocalDate date = parseLocalDateFromObject(dateObj);
            if (date == null) {
                return null;
            }
            FundMtss entity = fundMtssRepository.findByFundCodeAndDate(code, date)
                    .orElse(new FundMtss());
            entity.setFundCode(code);
            entity.setDate(date);
            entity.setFinancingBalance(parseBigDecimal(map.get("fin_value")));
            entity.setFinancingBuy(parseBigDecimal(map.get("fin_buy_value")));
            entity.setFinancingRepay(parseBigDecimal(map.get("fin_refund_value")));
            entity.setSecuritiesLendingBalance(parseBigDecimal(map.get("sec_value")));
            entity.setSecuritiesLendingSell(parseBigDecimal(map.get("sec_sell_value")));
            entity.setSecuritiesLendingRepay(parseBigDecimal(map.get("sec_refund_value")));
            entity.setMtssBalance(parseBigDecimal(map.get("fin_sec_value")));
            return entity;
        }).filter(Objects::nonNull)
          .filter(e -> e.getDate() != null)
          .toList();
        if (entities.isEmpty()) {
            return 0;
        }
        try {
            return fundMtssRepository.saveAll(entities).size();
        } catch (Exception e) {
            log.error("保存融资融券（MTSS）数据失败：{}", e.getMessage(), e);
            throw new RuntimeException("保存融资融券（MTSS）数据失败", e);
        }
    }

    //删除指定基金在某个时间范围内的采集数据
    @Transactional
    public void deleteDataInRange(String dataType, String fundCode, LocalDate startDate, LocalDate endDate) {
        if (startDate == null || endDate == null || endDate.isBefore(startDate)) {
            return;
        }
        switch (dataType) {
            case "FUND_NET_VALUE":
                fundNetValueHistoryRepository.deleteByFundCodeAndNetDateBetween(fundCode, startDate, endDate);
                break;
            case "FUND_FIN_INDICATOR":
                fundFinIndicatorRepository.deleteByFundCodeAndStatisticDateBetween(fundCode, startDate, endDate);
                break;
            case "FUND_DIVIDEND":
                fundDividendRepository.deleteByFundCodeAndPubDateBetween(fundCode, startDate, endDate);
                break;
            case "FUND_MF_DAILY_PROFIT":
                fundMfDailyProfitRepository.deleteByFundCodeAndEndDateBetween(fundCode, startDate, endDate);
                break;
            case "FUND_MTSS":
                fundMtssRepository.deleteByFundCodeAndDateBetween(fundCode, startDate, endDate);
                break;
            default:
                log.debug("数据类型 {} 暂不支持区间删除，已跳过清理操作", dataType);
        }
    }

    //为同一基金按日期计算 dailyGrowthRate。
    private void calculateDailyGrowthRate(List<FundNetValueHistory> entities) {
        if (entities == null || entities.isEmpty()) {
            return;
        }
        Map<String, List<FundNetValueHistory>> grouped =
                entities.stream()
                        .collect(Collectors.groupingBy(FundNetValueHistory::getFundCode));
        grouped.values().forEach(list -> {
            list.sort(Comparator.comparing(FundNetValueHistory::getNetDate));
            FundNetValueHistory prev = null;
            for (FundNetValueHistory curr : list) {
                if (prev == null) {
                    curr.setGrowthRate(BigDecimal.ZERO);
                } else {
                    BigDecimal today = curr.getUnitNav();
                    BigDecimal yesterday = prev.getUnitNav();
                    if (today != null && yesterday != null &&
                            yesterday.compareTo(BigDecimal.ZERO) != 0) {
                        BigDecimal growth = today
                                .divide(yesterday, 10, RoundingMode.HALF_UP)
                                .subtract(BigDecimal.ONE);
                        curr.setGrowthRate(growth);
                    } else {
                        curr.setGrowthRate(null);
                    }
                }
                prev = curr;
            }
        });
    }

}



